﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Schema;

[assembly: Dycox.Xml.XmlSchema("urn:effun-net-dycox-data-model", "Dycox.Xml.model.xsd")]
[assembly: Dycox.Xml.XmlSchema("urn:effun-net-dycox-data-view", "Dycox.Xml.view.xsd")]
[assembly: Dycox.Xml.XmlSchema("urn:effun-net-dycox-vstools-codegen", "Dycox.Xml.codegen.xsd")]

namespace Dycox.Xml
{
    public static class XmlSchemaLoader
    {
        private static Dictionary<string, XmlSchemaAttribute> _schemas;

        static XmlSchemaLoader()
        {
            _schemas = new Dictionary<string, XmlSchemaAttribute>();
            Assembly asm = Assembly.GetExecutingAssembly();

            FetchAssemblySchemas(asm);

        }

        public static XmlReader GetSchemaReader(string namespce)
        {
            return GetSchemaReader(namespce, Assembly.GetCallingAssembly());
        }

        public static XmlReader GetSchemaReader(string namespce, Assembly asm)
        {
            if (asm == null)
                throw new ArgumentNullException("asm");

            XmlSchemaAttribute attr = GetSchema(namespce, asm);

            return CreateSchemaReader(attr);

        }

        private static XmlSchemaAttribute GetSchema(string namespce, Assembly asm)
        {
            XmlSchemaAttribute attr;

            lock (_schemas)
            {

                if (!_schemas.TryGetValue(namespce, out attr))
                {
                    if (asm != null)
                    {
                        FetchAssemblySchemas(asm);
                        if (!_schemas.TryGetValue(namespce, out attr))
                            attr = null;
                    }
                    if (attr == null)
                        throw new ArgumentException(SR.GetString("schema_not_found", namespce));
                }
            }
            return attr;
        }


        private static void FetchAssemblySchemas(Assembly asm)
        {
            foreach (XmlSchemaAttribute attr in asm.GetCustomAttributes(typeof(XmlSchemaAttribute), false))
            {
                if (!_schemas.ContainsKey(attr.Namespace))
                {
                    attr.Assembly = asm;
                    _schemas.Add(attr.Namespace, attr);
                }
            }
        }

        private static XmlReader CreateSchemaReader(XmlSchemaAttribute attr)
        {
            Stream stream = attr.Assembly.GetManifestResourceStream(attr.ResourceName);
            if (stream == null)
                throw new InvalidOperationException(SR.GetString("resource_not_found", attr.ResourceName, attr.Assembly));

            XmlReaderSettings settings = new XmlReaderSettings();
            settings.CloseInput = true;

            return XmlReader.Create(stream, settings);
        }

        public static XmlReaderSettings CreateXmlReaderSettings(string schemaNamespace)
        {
            XmlReaderSettings settings;

            lock (_schemas)
            {
                XmlSchemaAttribute attr = GetSchema(schemaNamespace, Assembly.GetCallingAssembly());
                settings = new XmlReaderSettings();

                XmlSchemaSet schemaSet = new XmlSchemaSet();

                List<XmlSchemaAttribute> list = new List<XmlSchemaAttribute>();

                if (attr.Dependencies.Count > 0)
                {
                    GetDependencies(attr, list);
                }

                list.Add(attr);

                foreach (XmlSchemaAttribute a in list)
                {
                    schemaSet.Add(a.Namespace, CreateSchemaReader(a));
                }

                settings.Schemas = schemaSet;
                settings.ValidationType = ValidationType.Schema;
            }

            return settings;
        }

        private static void GetDependencies(XmlSchemaAttribute attr, List<XmlSchemaAttribute> list)
        {
            foreach (string depns in attr.Dependencies)
            {
                XmlSchemaAttribute da = GetSchema(depns, null);
                if (da.Dependencies.Count > 0)
                    GetDependencies(da, list);

                if (!list.Any(o => o.Namespace == da.Namespace))
                    list.Add(da);
            }
        }
    }
}
